//
// SoapTrace.Net - A soap message monitoring utility. 
//
// Copyright (C) 2005-2009 Matthew Ward
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Matthew Ward (mrward@users.sourceforge.net)

using SoapTrace.Core;
using SoapTrace.Core.Communication;
using SoapTrace.Core.Formatters;
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;

namespace SoapTrace
{
	/// <summary>
	/// Displays a list of soap messages and the selected soap request
	/// and response.
	/// </summary>
    public partial class SoapMessagesView : System.Windows.Forms.UserControl
    {
        /// <summary>
        /// A reference to the manager.
        /// </summary>
        private MonitorManager m_Manager;

        /// <summary>
        /// Append item method's delegate so the method can be
        /// called from another thread.
        /// </summary>
        private delegate void AppendItemInvoker(SoapRequestAndResponse item);

        /// <summary>
        /// Gets the selected soap request and response item.
        /// </summary>
        private delegate SoapRequestAndResponse GetSelectedItemInvoker();

        /// <summary>
        /// The formatted soap request.
        /// </summary>
        private FormattedText m_Request;

        /// <summary>
        /// The formatted soap response.
        /// </summary>
        private FormattedText m_Response;

        /// <summary>
        /// Flag indicating that any changes to the request text are
        /// from the user.
        /// </summary>
        private bool m_UserChangingRequestText = true;

        /// <summary>
        /// Flag indicating that the currently on display request
        /// text has been changed by the user.
        /// </summary>
        private bool m_RequestTextChanged;

        /// <summary>
        /// Creates a new instance of the <see cref="SoapMessagesView"/>
        /// control.
        /// </summary>
        public SoapMessagesView()
        {
            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();
        }

        /// <summary>
        /// Gets or sets the list view's context menu.
        /// </summary>
        public ContextMenu ListViewContextMenu
        {
            get
            {
                return MessagesListView.ContextMenu;
            }

            set
            {
                MessagesListView.ContextMenu = value;
            }
        }

        /// <summary>
        /// Gets or sets the request text box's context menu.
        /// </summary>
        public ContextMenu RequestTextBoxContextMenu
        {
            get
            {
                return RequestTextBox.ContextMenu;
            }

            set
            {
                RequestTextBox.ContextMenu = value;
            }
        }

        /// <summary>
        /// Gets or sets the response text box's context menu.
        /// </summary>
        public ContextMenu ResponseTextBoxContextMenu
        {
            get
            {
                return ResponseTextBox.ContextMenu;
            }

            set
            {
                ResponseTextBox.ContextMenu = value;
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="MonitorManager"/>
        /// that this control will use.
        /// </summary>
        [Browsable(false)]
        public MonitorManager Manager
        {
            get
            {
                return m_Manager;
            }

            set
            {
                m_Manager = value;
            }
        }

        /// <summary>
        /// Gets the currently selected soap request and response item.
        /// </summary>
        [Browsable(false)]
        public SoapRequestAndResponse SelectedItem
        {
            get
            {
                if (InvokeRequired)
                {
                    GetSelectedItemInvoker Invoker = new GetSelectedItemInvoker(GetSelectedItem);
                    return (SoapRequestAndResponse)Invoke(Invoker);
                }
                else
                {
                    return GetSelectedItem();
                }
            }
        }

        private SoapRequestAndResponse GetSelectedItem()
        {
            if (MessagesListView.SelectedItems.Count > 0)
            {
                return (SoapRequestAndResponse)(MessagesListView.SelectedItems[0].Tag);
            }

            return null;
        }

        /// <summary>
        /// Gets whether there are any selected items.
        /// </summary>
        [Browsable(false)]
        public bool HasSelectedItems
        {
            get
            {
                return (MessagesListView.SelectedItems.Count > 0);
            }
        }

        /// <summary>
        /// Appends an item to the list view.
        /// </summary>
        /// <param name="item">The soap request and response item to append.</param>
        public void AppendItem(SoapRequestAndResponse item)
        {
            if (InvokeRequired)
            {
                AppendItemInvoker Delegate = new AppendItemInvoker(AppendItem);
                try
                {

                    Invoke(Delegate, new object[] { item });
                }
                catch { }
            }
            else
            {
                ListViewItem ListViewItem =
                    MessagesListView.Items.Add(item.TimeReceived.ToString("HH:mm:ss yyyy-MM-dd", CultureInfo.InvariantCulture));
                ListViewItem.SubItems.Add(item.WebMethod);
                ListViewItem.SubItems.Add(item.WebService);
                ListViewItem.SubItems.Add(item.Source);
                
                ListViewItem.Tag = item;

                if (FiltredListView.Visible && SearchTextBox.Text.Length > 0)
                {
                    string s = SearchTextBox.Text.ToLower();

                    filterItem(ListViewItem, s);
                    if (ListViewItem.BackColor == Color.LightGreen)
                    {
                        var clone = (ListViewItem)ListViewItem.Clone();
                        clone.BackColor = Color.White;
                        FiltredListView.Items.Add(clone);
                    }
                }


            }
        }

        /// <summary>
        /// Clears all the items displayed on the form.
        /// </summary>
        public void Clear()
        {
            MessagesListView.Items.Clear();

            m_UserChangingRequestText = false;

            RequestTextBox.Text = String.Empty;
            ResponseTextBox.Text = String.Empty;

            m_UserChangingRequestText = true;

            // Request text no longer dirty.
            m_RequestTextChanged = false;
        }

        /// <summary>
        /// Gets or sets the formatted soap request.
        /// </summary>
        [Browsable(false)]
        internal FormattedText Request
        {
            get
            {
                return m_Request;
            }

            set
            {
                m_Request = value;

                // Request text no longer dirty.
                m_RequestTextChanged = false;

                try
                {
                    RequestTextBox.TextChanged -= new EventHandler(RequestTextBoxTextChanged);
                    RichTextBoxFormatter.Format(RequestTextBox, value);
                }
                finally
                {
                    RequestTextBox.TextChanged += new EventHandler(RequestTextBoxTextChanged);
                }
            }
        }

        /// <summary>
        /// Gets or sets the formatted soap response.
        /// </summary>
        [Browsable(false)]
        internal FormattedText Response
        {
            get
            {
                return m_Response;
            }

            set
            {
                m_Response = value;

                RichTextBoxFormatter.Format(ResponseTextBox, value);
            }
        }

        /// <summary>
        /// Gets or sets the soap message request text on display.
        /// </summary>
        [Browsable(false)]
        public string RequestText
        {
            [DebuggerStepThrough]
            set
            {
                m_UserChangingRequestText = false;
                RequestTextBox.Text = value;
                m_UserChangingRequestText = true;

                // Request text no longer dirty.
                m_RequestTextChanged = false;
            }

            [DebuggerStepThrough]
            get
            {
                return RequestTextBox.Text;
            }
        }

        /// <summary>
        /// Gets whether the request text has been changed by
        /// the user.
        /// </summary>
        [Browsable(false)]
        public bool RequestTextChanged
        {
            get
            {
                return m_RequestTextChanged;
            }
        }

        /// <summary>
        /// Gets or sets the read-only flag on the soap request's
        /// text box.
        /// </summary>
        public bool RequestTextReadOnly
        {
            get
            {
                return RequestTextBox.ReadOnly;
            }

            set
            {
                RequestTextBox.ReadOnly = value;
            }
        }

        /// <summary>
        /// Gets or sets the soap message response text on display.
        /// </summary>
        [Browsable(false)]
        public string ResponseText
        {
            [DebuggerStepThrough]
            set
            {
                ResponseTextBox.Text = value;
            }

            [DebuggerStepThrough]
            get
            {
                return ResponseTextBox.Text;
            }
        }

        /// <summary>
        /// Gets or sets the current text word wrap flag.
        /// </summary>
        public bool WordWrap
        {
            [DebuggerStepThrough]
            get
            {
                return RequestTextBox.WordWrap;
            }

            set
            {
                RequestTextBox.WordWrap = value;
                ResponseTextBox.WordWrap = value;
            }
        }

        /// <summary>
        /// Gets or sets the font to be used to display the soap message text.
        /// </summary>
        public Font TextFont
        {
            get
            {
                return RequestTextBox.Font;
            }

            set
            {
                try
                {
                    m_UserChangingRequestText = false;

                    RequestTextBox.Font = value;
                    ResponseTextBox.Font = value;
                }
                finally
                {
                    m_UserChangingRequestText = true;
                }
            }
        }

        /// <summary>
        /// Gets or sets the font to use in the list view.
        /// </summary>
        public Font ListFont
        {
            get
            {
                return MessagesListView.Font;
            }

            set
            {
                MessagesListView.Font = value;
            }
        }

        /// <summary>
        /// Gets or sets the width of the panel containing the soap
        /// messages list view.
        /// </summary>
        public int SoapMessagesPanelWidth
        {
            get
            {
                return MessagesListViewPanel.Width;
            }

            set
            {
                MessagesListViewPanel.Width = value;
            }
        }

        /// <summary>
        /// Gets or sets the request text box panel's height.
        /// </summary>
        public int RequestPanelHeight
        {
            get
            {
                return RequestPanel.Height;
            }

            set
            {
                RequestPanel.Height = value;
            }
        }

        /// <summary>
        /// Gets or sets the width of the time column.
        /// </summary>
        public int TimeColumnHeaderWidth
        {
            get
            {
                return TimeColumnHeader.Width;
            }

            set
            {
                TimeColumnHeader.Width = value;
            }
        }

        /// <summary>
        /// Gets or sets the width of web service operation column.
        /// </summary>
        public int WebServiceOperationColumnHeaderWidth
        {
            get
            {
                return WebServiceOperationColumnHeader.Width;
            }

            set
            {
                WebServiceOperationColumnHeader.Width = value;
            }
        }

        /// <summary>
        /// Gets or sets the width of web service column.
        /// </summary>
        public int WebServiceColumnHeaderWidth
        {
            get
            {
                return WebServiceColumnHeader.Width;
            }

            set
            {
                WebServiceColumnHeader.Width = value;
            }
        }

        /// <summary>
        /// Gets or sets the width of the source column.
        /// </summary>
        public int SourceColumnHeaderWidth
        {
            get
            {
                return SourceColumnHeader.Width;
            }

            set
            {
                SourceColumnHeader.Width = value;
            }
        }

        /// <summary>
        /// Determines whether a text box is currently active.
        /// </summary>
        public bool IsTextBoxActive
        {
            get
            {
                bool Active = false;

                RichTextBox ActiveTextBox = ActiveControl as RichTextBox;

                if (ActiveTextBox != null)
                {
                    Active = true;
                }

                return Active;
            }
        }

        /// <summary>
        /// Gets the currently selected text in the active
        /// text box.
        /// </summary>
        public string SelectedText
        {
            get
            {
                string Text = String.Empty;

                RichTextBox ActiveTextBox = ActiveControl as RichTextBox;

                if (ActiveTextBox != null)
                {
                    Text = ActiveTextBox.SelectedText;
                }

                return Text;
            }
        }

        /// <summary>
        /// Attempts to find the text the user searched for and 
        /// highlights it.
        /// </summary>
        /// <param name="searchText">The text to search for.</param>
        /// <param name="caseSensitive">A flag indicating whether the
        /// search is case sensitive.</param>
        /// <returns>A flag indicating whether the search was successful.</returns>
        public bool FindText(string searchText, bool caseSensitive)
        {
            bool Found = false;

            RichTextBox ActiveTextBox = ActiveControl as RichTextBox;

            if (ActiveTextBox != null)
            {
                Found = FindText(ActiveTextBox, searchText, caseSensitive);
            }

            return Found;
        }

        /// <summary>
        /// Selects all the text in the current active text box.
        /// </summary>
        public void SelectAllText()
        {
            RichTextBox ActiveTextBox = ActiveControl as RichTextBox;

            if (ActiveTextBox != null)
            {
                ActiveTextBox.SelectAll();
            }
        }

        /// <summary>
        /// Copies the currently selected text to the clipboard.
        /// </summary>
        public void CopyText()
        {
            RichTextBox ActiveTextBox = ActiveControl as RichTextBox;

            if (ActiveTextBox != null)
            {
                ActiveTextBox.Copy();
            }
        }

        /// <summary>
        /// Displays the soap message request and response after the
        /// user selects an item in the list view.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event's arguments.</param>
        private void MessagesListViewSelectedIndexChanged(object sender, EventArgs e)
        {
            var listView = sender as ListView;

            if (listView.SelectedItems.Count > 0)
            {
                ListViewItem ItemSelected = listView.SelectedItems[0];

                if (ItemSelected != null)
                {
                    m_Manager.ItemSelected((SoapRequestAndResponse)ItemSelected.Tag);
                
                }
            }
        }

        /// <summary>
        /// User has altered the request text.
        /// </summary>
        /// <param name="sender">The event source.</param>
        /// <param name="e">The event arguments.</param>
        private void RequestTextBoxTextChanged(object sender, EventArgs e)
        {
            if (m_UserChangingRequestText)
            {
                m_RequestTextChanged = true;
            }
        }

        /// <summary>
        /// Attempts to find the text the user searched for and highlights it.
        /// </summary>
        /// <param name="textBox">The text box to search.</param>
        /// <param name="searchText">The text to search for.</param>
        /// <param name="caseSensitive">A flag indicating whether the
        /// search is case sensitive.</param>
        /// <returns>A flag indicating whether the search was successful.</returns>
        private static bool FindText(RichTextBox textBox, string searchText, bool caseSensitive)
        {
            bool Found = false;

            // Try to find text from the cursor position.

            int StartIndex = textBox.SelectionStart + textBox.SelectionLength;

            Found = FindTextFromIndex(textBox, searchText, StartIndex, caseSensitive);

            if (Found == false)
            {
                // Try from beginning of the text.

                Found = FindTextFromIndex(textBox, searchText, 0, caseSensitive);
            }

            
            textBox.ScrollToCaret();

            return Found;
        }

        /// <summary>
        /// Finds the text from the cursor position and highlights it.
        /// </summary>
        /// <param name="textBox">The text box to find the text in.</param>
        /// <param name="searchText">The text to search for.</param>
        /// <param name="startIndex">The starting cursor position to 
        /// begin the search.</param>
        /// <param name="caseSensitive">A flag indicating whether the search
        /// is case sensitive.</param>
        /// <returns>A flag indicating whether the find was successful.</returns>
        private static bool FindTextFromIndex(RichTextBox textBox,
            string searchText,
            int startIndex,
            bool caseSensitive)
        {
            bool Found = false;

            string TextToSearch = textBox.Text;

            if (caseSensitive == false)
            {
                TextToSearch = TextToSearch.ToLower(CultureInfo.InvariantCulture);
                searchText = searchText.ToLower(CultureInfo.InvariantCulture);
            }

            int IndexFound = TextToSearch.IndexOf(searchText, startIndex);

            if (IndexFound != -1)
            {
                textBox.SelectionStart = IndexFound;
                textBox.SelectionLength = searchText.Length;
                               
                textBox.SelectionColor =  Color.Red;
                
                Found = true;
            }

            return Found;
        }

        private void SearchTextBox_TextChanged(object sender, EventArgs e)
        {
                        
            string s = SearchTextBox.Text.ToLower();

            bool filterOn = s.Length > 0;

       
            foreach (ListViewItem i in MessagesListView.Items)
            {

             
                if (s.Equals(string.Empty))
                {
                    i.BackColor = Color.White;
                    continue;
                }

                filterItem(i, s);
            
            }

          

        }


        private bool filterItem(ListViewItem i, string s)
        {
            if (searchInReq.Checked)
                if ((i.Tag as SoapRequestAndResponse).Request.Message.ToLower().Contains(s))
                {
                    i.BackColor = Color.LightGreen;
                    return true;
                }

            if (searchInResp.Checked)
                if ((i.Tag as SoapRequestAndResponse).Response.Message.ToLower().Contains(s))
                {
                    i.BackColor = Color.LightGreen;
                    return true;
                }


            if (i.SubItems[1].Text.ToLower().Contains(s))
            {
                i.BackColor = Color.LightGreen;
            }
            else
            {
                i.BackColor = Color.White;
            }

            return true;
        }
        private void button1_Click(object sender, EventArgs e)
        {

            ListView lv = FiltredListView.Visible ? FiltredListView : MessagesListView;

            if (lv.SelectedItems.Count == 0)
            {
                return;
            }

            var item = (lv.SelectedItems[0].Tag as SoapRequestAndResponse);

            string text = "---------------- REQUEST (" + item.Request.WebMethod + ") ------------------\n\n\n";

            text += RequestTextBox.Text;

            text += "\n\n\n\n";
            text += "---------------- RESPONSE (" + item.Response.WebMethod + ") ------------------\n\n\n";

            text += ResponseTextBox.Text;

            Clipboard.Clear();
            Clipboard.SetText(text);
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
      
            string s = SearchTextBox.Text.ToLower();

            bool filterOn = checkBox1.Checked;

            FiltredListView.Visible = filterOn;
            MessagesListView.Visible = !filterOn;
            
            if (filterOn)
            {

     


                FiltredListView.Items.Clear();

                if (s.Length == 0)
                    return;

                for (int i = 0; i < MessagesListView.Items.Count; i++ )
                {
                    var item = (ListViewItem)MessagesListView.Items[i];

                    if (item.BackColor == Color.LightGreen)
                    {
                        item.BackColor = Color.White;
                        FiltredListView.Items.Add((ListViewItem)MessagesListView.Items[i].Clone());
                    }
                }
            }            

        }

        private void RefreshFilter_Click(object sender, EventArgs e)
        {

            SearchTextBox_TextChanged(SearchTextBox, null);
            if (checkBox1.Checked)
            {
                checkBox1_CheckedChanged(checkBox1, null);
            }
            
                
        }
        


    }
}
